package editor;

import org.tmatesoft.svn.core.SVNCommitInfo;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
import org.tmatesoft.svn.core.wc.SVNClientManager;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNUpdateClient;

import java.io.File;

/**
 * This class manages subversion (SVN) actions.  It is almost all based directly from
 * code on the SVNKit wiki, specifically https://wiki.svnkit.com/Managing_A_Working_Copy
 *
 * @author Neil Dickson
 */
public class SVNController {
	private static SVNClientManager CLIENT_MANAGER = SVNClientManager.newInstance();
	private static boolean isInitialised = false;

	/**
	 * This sets up SVNKit to support various different protocols.
	 * It MUST be called in order for SVNKit to work with anything other than the working copy.
	 */
	public static void ensureInitialised() {
		if (!isInitialised) {
			isInitialised = true;
			// Set up connection protocols support:
			// http:// and https://
			DAVRepositoryFactory.setup();
			// svn://, svn+xxx:// (svn+ssh:// in particular)
			SVNRepositoryFactoryImpl.setup();
			// file:///
			FSRepositoryFactory.setup();
		}
	}

	public static SVNCommitInfo makeDirectory(SVNURL url, String commitMessage) throws SVNException {
		ensureInitialised();
		return CLIENT_MANAGER.getCommitClient().doMkDir(new SVNURL[]{url},commitMessage);
	}

	public static SVNCommitInfo importDirectory(File localPath, SVNURL dstURL, String commitMessage, boolean isRecursive) throws SVNException {
		ensureInitialised();
		return CLIENT_MANAGER.getCommitClient().doImport(localPath,dstURL,commitMessage,isRecursive);
	}

	/**
	 * This commits a (local) working copy to its (remote) repository.
	 * @param wcPath path of the working copy
	 * @param keepLocks true iff the committing user is to keep any locks on files
	 * @param commitMessage message to describe the committed changes
	 * @return information on the committed revision
	 * @throws SVNException if the commit fails
	 */
	public static SVNCommitInfo commit(File wcPath, boolean keepLocks, String commitMessage) throws SVNException {
		ensureInitialised();
		return CLIENT_MANAGER.getCommitClient().doCommit(new File[]{wcPath},keepLocks,commitMessage,false,true);
	}

	/**
	 * This checks out a (remote) repository to a (local) working copy.
	 * @param url URL of the repository
	 * @param revision revision to be checked out ({@link SVNRevision#HEAD} for latest)
	 * @param wcPath path for the working copy to be
	 * @param isRecursive true iff checkout is to recurse through the repository
	 * @return the new revision number of the working copy
	 * @throws SVNException if the checkout fails
	 */
	public static long checkout(SVNURL url, SVNRevision revision, File wcPath, boolean isRecursive) throws SVNException {
		ensureInitialised();
		SVNUpdateClient updateClient = CLIENT_MANAGER.getUpdateClient();
		// Sets externals not to be ignored during the checkout
		updateClient.setIgnoreExternals(false);
		return updateClient.doCheckout(url,wcPath,revision,revision,isRecursive);
	}

	/**
	 * This updates a (local) working copy from a (remote) repository.
	 * @param wcPath path of the working copy
	 * @param revision revision to which to update
	 * @param isRecursive true iff update is to recurse through the repository
	 * @return the new revision number of the working copy
	 * @throws SVNException if the update fails
	 */
	public static long update(File wcPath, SVNRevision revision, boolean isRecursive) throws SVNException {
		ensureInitialised();
		SVNUpdateClient updateClient = CLIENT_MANAGER.getUpdateClient();
		// Sets externals not to be ignored during the update
		updateClient.setIgnoreExternals(false);
		return updateClient.doUpdate(wcPath,revision,isRecursive);
	}

	public static long switchToURL(File wcPath, SVNURL url, SVNRevision updateToRevision, boolean isRecursive) throws SVNException {
		ensureInitialised();
		SVNUpdateClient updateClient = CLIENT_MANAGER.getUpdateClient();
		// sets externals not to be ignored during the switch
		updateClient.setIgnoreExternals(false);
		// returns the number of the revision wcPath was updated to
		return updateClient.doSwitch(wcPath,url,updateToRevision,isRecursive);
	}

	/**
	 * This adds the specified file or directory to be managed by version control (but does not commit it to the repository).
	 * @param wcPath path of the file to be added
	 * @throws SVNException if the add fails
	 */
	public static void addEntry(File wcPath) throws SVNException {
		ensureInitialised();
		CLIENT_MANAGER.getWCClient().doAdd(wcPath,false,false,false,true);
	}

	public static void lock(File wcPath, boolean isStealLock, String lockComment) throws SVNException {
		ensureInitialised();
		CLIENT_MANAGER.getWCClient().doLock(new File[]{wcPath},isStealLock,lockComment);
	}

	/**
	 * This schedules the spcified file or directory for deletion from version control and deletes it from the filesystem (but does not commit the deletion to the repository).
	 * @param wcPath path of the file to be deleted
	 * @param force true iff want to force deletion, even if directory not empty
	 * @throws SVNException if the deletion fails
	 */
	public static void delete(File wcPath, boolean force) throws SVNException {
		ensureInitialised();
		CLIENT_MANAGER.getWCClient().doDelete(wcPath,force,false);
	}

	/**
	 * This copies or moves the specified repository URL to another repository URL, committing the change immediately.
	 * @param srcURL source repository URL
	 * @param dstURL destination repository URL
	 * @param isMove true to do a move; false to do a copy
	 * @param commitMessage message to describe the committed changes
	 * @return information on the committed revision
	 * @throws SVNException if the copy/move fails
	 */
	public static SVNCommitInfo copy(SVNURL srcURL, SVNURL dstURL, boolean isMove, String commitMessage) throws SVNException {
		ensureInitialised();
		return CLIENT_MANAGER.getCopyClient().doCopy(srcURL,SVNRevision.HEAD,dstURL,isMove,commitMessage);
	}
}
